﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections.ObjectModel;

namespace SanctusSoftware.CcliSongSelectLib.Management
{
    public class LibraryManager
    {

        #region Properties

        private readonly ObservableCollection<string> _Files = new ObservableCollection<string>();

        private ReadOnlyObservableCollection<string> _ReadOnlyFiles = null;
        private object _ReadOnlyFilesLockObject = new object();

        public ReadOnlyObservableCollection<string> Files
        {
            get
            {
                lock (_ReadOnlyFilesLockObject)
                {
                    if (_ReadOnlyFiles == null)
                    {
                        _ReadOnlyFiles = new ReadOnlyObservableCollection<string>(_Files);
                    } 
                }
                return _ReadOnlyFiles;
            }
        }

        #endregion

        #region Constructor

        public LibraryManager()
        {

        }

        #endregion

        #region Methods (Library Management)

        //private readonly LibraryCollection _Libraries = new LibraryCollection();
        private readonly Dictionary<Library, FileSystemWatcher> _Libraries = new Dictionary<Library, FileSystemWatcher>();

        public void Add(Library library)
        {
            if (_Libraries.ContainsKey(library))
            {
                // TODO:
                throw new Exception();
            }

            // TODO: Check if the library overlaps

            FileSystemWatcher watcher = new FileSystemWatcher();
            //watcher.Changed += new FileSystemEventHandler(watcher_Changed);
            watcher.Created += new FileSystemEventHandler(watcher_Created);
            watcher.Deleted += new FileSystemEventHandler(watcher_Deleted);
            watcher.Renamed += new RenamedEventHandler(watcher_Renamed);
            _Libraries.Add(library, watcher);

            watcher.Filter = this.FileSearchPattern;
            watcher.Path = library.Path;
            watcher.IncludeSubdirectories = library.IncludeSubdirectories;
            
            var searchOption = library.IncludeSubdirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
            var files = Directory.GetFiles(library.Path, this.FileSearchPattern, searchOption);
            foreach (var file in files)
            {
                AddFile(file, library);
            }

            watcher.EnableRaisingEvents = true;

            library.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(library_PropertyChanged);



        }





        public Library Add(string path, bool includeSubDirectories)
        {
            // TODO: see if path is already included
            Library lib = new Library() { Path = path, IncludeSubdirectories = includeSubDirectories };
            this.Add(lib);
            return lib;
        }

        void library_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            Library library = sender as Library;
            if (library != null)
            {

                FileSystemWatcher watcher = _Libraries[library];

                // TODO: figure out how to remove existing items

                switch (e.PropertyName)
                {
                    case "Path":
                        watcher.Path = library.Path;
                        break;
                    case "IncludeSubdirectories":
                        watcher.IncludeSubdirectories = library.IncludeSubdirectories;
                        break;
                    default:
                        break;
                }
            }
        }

        #endregion

        #region Methods (File Management)

        // File, Libraries
        private readonly FileToLibraryCollection _FilesToLibraries = new FileToLibraryCollection();

        

        protected virtual bool IsFileValid(string file)
        {
            return true;
        }

        protected virtual string FileSearchPattern
        {
            get { return "*.*"; }
        }

        private void AddFile(string filePath, FileSystemWatcher watcher)
        {
            AddFile(filePath, GetLibraryFromWatcher(watcher));
        }

        private void AddFile(string filePath, Library library)
        {
            if (IsFileValid(filePath))
            {

                if (_FilesToLibraries.Contains(filePath))
                {
                    if (!_FilesToLibraries[filePath].Libraries.Contains(library))
                    {
                        _FilesToLibraries[filePath].Libraries.Add(library);
                    }
                }
                else
                {
                    _FilesToLibraries.Add(filePath, library);
                    _Files.Add(filePath);
                    // Event or virtual method or both
                }
            }
        }

        private void RemoveFile(string filePath, FileSystemWatcher watcher)
        {
            RemoveFile(filePath, GetLibraryFromWatcher(watcher));            
        }

        private void RemoveFile(string filePath, Library library)
        {
            if (_FilesToLibraries.Contains(filePath, library))
            {
                FileToLibrary f = _FilesToLibraries[filePath];
                f.Libraries.Remove(library);
                if (f.Libraries.Count == 0)
                {
                    _FilesToLibraries.Remove(f);
                    _Files.Remove(filePath);
                    // Event or Virtual Method or both
                }
            }
        }

        private void RenameFile(string oldFullPath, string fullPath, FileSystemWatcher watcher)
        {
            // it it didn't used to be the desired file type but is now, add
            // it it used to be the desired file type but not is  NOT, remove
            // if it was and is the desired file type, rename it in the Files collection
        }


        private Library GetLibraryFromWatcher(FileSystemWatcher watcher)
        {
            var kvp = _Libraries.FirstOrDefault(item => item.Value == watcher);
            if (kvp.Value == null)
            {
                throw new ArgumentException();
            }
            return kvp.Key;
        }

        void watcher_Deleted(object sender, FileSystemEventArgs e)
        {
            bool isFile = true; // TODO: figure out if its a file or directory
            FileSystemWatcher watcher = sender as FileSystemWatcher;
            RemoveFile(e.FullPath, watcher);
        }

        void watcher_Created(object sender, FileSystemEventArgs e)
        {
            bool isFile = true; // TODO: figure out if its a file or directory
            FileSystemWatcher watcher = sender as FileSystemWatcher;
            AddFile(e.FullPath, watcher);
        }

        void watcher_Renamed(object sender, RenamedEventArgs e)
        {
            bool isFile = true; // TODO: figure out if its a file or directory
            FileSystemWatcher watcher = sender as FileSystemWatcher;
            RenameFile(e.OldFullPath, e.FullPath, watcher); 
        }

        //void watcher_Changed(object sender, FileSystemEventArgs e)
        //{

        //    // This event isn't firing for some reason

        //    bool isFile = true; // TODO: figure out if its a file or directory
        //    FileSystemWatcher watcher = sender as FileSystemWatcher;

        //    if (isFile && watcher != null)
        //    {


        //        switch (e.ChangeType)
        //        {
        //            case WatcherChangeTypes.All:
        //                break;
        //            case WatcherChangeTypes.Changed:
        //                // TODO: Reload the file? Yeah
        //                break;
        //            case WatcherChangeTypes.Created:
        //                AddFile(e.FullPath, watcher);
        //                break;
        //            case WatcherChangeTypes.Deleted:
        //                RemoveFile(e.FullPath, watcher);
        //                break;
        //            case WatcherChangeTypes.Renamed:
        //                RenameFile(e.FullPath, watcher);
        //                break;
        //            default:
        //                break;
        //        }
        //    }
        //}


        #endregion

    }
}
